/* Nyuのじゃじゃじゃ版
 */

%{

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nyu_common.h"
#include "nyu_tree.h"
#include "nyu_scope.h"
#include "parse.h"
#include "nyu_object.h"
#include "nyu_string.h"
#include "nyu_array.h"
#include "nyu_range.h"

extern FILE *yyin;
extern int yylineno;

/* 処理中のファイル名 */
const char *input_filename = NULL;

/* 現在のスコープ */
NyuScope *current_scope = NULL;
    
int yylex(YYSTYPE *lv);
void yyerror(const char *msg);

long eval_tree(NyuTree *tree, int is_tail);

%}

%pure_parser
%verbose

%union {
    long         intval;
    double       fltval;
    char        *strval;
    NyuTree     *tree;
    NyuTreeList *list;
}

%token NYU_TOK_ERROR

%token
    NYU_TOK_EQUAL                "="
    NYU_TOK_EQUAL2               "=="
    NYU_TOK_EQUAL3               "==="
    NYU_TOK_EXCLAMATION          "!"
    NYU_TOK_EXCLAMATION_EQUAL    "!="
    NYU_TOK_EXCLAMATION_EQUAL2   "!=="
    NYU_TOK_PLUS                 "+"
    NYU_TOK_PLUS2                "++"
    NYU_TOK_PLUS_EQUAL           "+="
    NYU_TOK_MINUS                "-"
    NYU_TOK_MINUS2               "--"
    NYU_TOK_MINUS_EQUAL          "-="
    NYU_TOK_ASTERISK             "*"
    NYU_TOK_ASTERISK_EQUAL       "*="
    NYU_TOK_SLASH                "/"
    NYU_TOK_SLASH_EQUAL          "/="
    NYU_TOK_PERCENT              "%"
    NYU_TOK_PERCENT_EQUAL        "%="
    NYU_TOK_LESS                 "<"
    NYU_TOK_LESS2                "<<"
    NYU_TOK_LESS_EQUAL           "<="
    NYU_TOK_LESS2_EQUAL          "<<="
    NYU_TOK_GREATER              ">"
    NYU_TOK_GREATER2             ">>"
    NYU_TOK_GREATER_EQUAL        ">="
    NYU_TOK_GREATER2_EQUAL       ">>="
    NYU_TOK_LESS_EQUAL_GREATER   "<=>"
    NYU_TOK_LESS_MINUS_GREATER   "<->"
    NYU_TOK_PIPE                 "|"
    NYU_TOK_PIPE2                "||"
    NYU_TOK_PIPE_EQUAL           "|="
    NYU_TOK_PIPE2_EQUAL          "||="
    NYU_TOK_HAT                  "^"
    NYU_TOK_HAT2                 "^^"
    NYU_TOK_HAT_EQUAL            "^="
    NYU_TOK_AMPERSAND            "&"
    NYU_TOK_AMPERSAND2           "&&"
    NYU_TOK_AMPERSAND_EQUAL      "&="
    NYU_TOK_AMPERSAND2_EQUAL     "&&="
    NYU_TOK_TILDE                "~"
    NYU_TOK_TILDE2               "~~"
    NYU_TOK_QUESTION             "?"
    NYU_TOK_COLON                ":"
    NYU_TOK_COLON_EQUAL          ":="
    NYU_TOK_SEMICOLON            ";"
    NYU_TOK_DOT                  "."
    NYU_TOK_DOT2                 ".."
    NYU_TOK_DOT3                 "..."
    NYU_TOK_COMMA                ","
    NYU_TOK_LPARENTHESIS         "("
    NYU_TOK_RPARENTHESIS         ")"
    NYU_TOK_LBRACKET             "["
    NYU_TOK_RBRACKET             "]"
    NYU_TOK_LBRACE               "{"
    NYU_TOK_RBRACE               "}"
    NYU_TOK_LBRACE_PERCENT       "{%"
    NYU_TOK_PERCENT_RBRACE       "%}"

%token NYU_TOK_ARGLEN NYU_TOK_CURBLK
%token<intval> NYU_TOK_INTVAL NYU_TOK_ARGREF
%token<fltval> NYU_TOK_FLTVAL
%token<strval> NYU_TOK_STRVAL NYU_TOK_SYMBOL

%type<tree> Statement Expression AssignExpression
    CondExpression LOrExpression LXorExpression LAndExpression BOrExpression
    BXorExpression BAndExpression EqualExpression RelExpression CmpExpression
    OwnExpression ShiftExpression PlsMnsExpression MulDivModExpression
    UnaryExpression PostfixExpression PrimaryExpression IntLiteral FloatLiteral
    StringLiteral BlockLiteral ObjectCreation ObjectSlot
    ArrayCreation RangeCreation Identifier SlotAccess Call

%type<list> Statements Expressions ObjectSlotList

%%

nyu:
/* nil */ {
}
|Statements SemicolonOpt {
    NyuTreeList *l;

    //nyu_tree_dump(NYU_TREE($1));

    for (l = nyu_tree_list_reverse($1); l; l = l->next) {
        //printf("# => %ld\n", eval_tree(l->value, !l->next) >> 2);
        eval_tree(l->value, 0);
    }
}
;

Statements:
";" {
    $$ = NULL;
}
|Statement {
    if ($1)
        $$ = nyu_tree_list_prepend(NULL, $1);
    else
        $$ = NULL;
}
|Statements ";" Statement {
    if ($3)
        $$ = nyu_tree_list_prepend($1, $3);
    else
        $$ = $1;
}
;

Statement:
Expression {
    /* Nothing to do. */
}
;

Expression:
AssignExpression {
    /* Nothing to do. */
}
;

AssignExpression:
CondExpression {
    /* Nothing to do. */
}
|CondExpression "=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_PUT, $1, $3);
}
|CondExpression ":=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_SET, $1, $3);
}
|CondExpression "+=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_ADD_ASSIGN, $1, $3);
}
|CondExpression "-=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_SUB_ASSIGN, $1, $3);
}
|CondExpression "*=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_MUL_ASSIGN, $1, $3);
}
|CondExpression "/=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_DIV_ASSIGN, $1, $3);
}
|CondExpression "%=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_MOD_ASSIGN, $1, $3);
}
|CondExpression "<<=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_LSHIFT_ASSIGN, $1, $3);
}
|CondExpression ">>=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_RSHIFT_ASSIGN, $1, $3);
}
|CondExpression "&=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_BAND_ASSIGN, $1, $3);
}
|CondExpression "^=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_BXOR_ASSIGN, $1, $3);
}
|CondExpression "|=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_BOR_ASSIGN, $1, $3);
}
|CondExpression "&&=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_LAND_ASSIGN, $1, $3);
}
|CondExpression "||=" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_LOR_ASSIGN, $1, $3);
}
|CondExpression "<->" AssignExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_SWAP, $1, $3);
}
;

CondExpression:
LOrExpression {
    /* Nothing to do. */
}
|LOrExpression "?" Expression ":" CondExpression {
    $$ = nyu_tree_cond_new($1, $3, $5);
}
;

LOrExpression:
LXorExpression {
    /* Nothing to do. */
}
|LOrExpression "||" LXorExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_LOR, $1, $3);
}
;

LXorExpression:
LAndExpression {
    /* Nothing to do. */
}
|LXorExpression "^^" LAndExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_LXOR, $1, $3);
}
;

LAndExpression:
BOrExpression {
    /* Nothing to do. */
}
|LAndExpression "&&" BOrExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_LAND, $1, $3);
}
;

BOrExpression:
BXorExpression {
    /* Nothing to do. */
}
|BOrExpression "|" BXorExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_BOR, $1, $3);
}
;

BXorExpression:
BAndExpression {
    /* Nothing to do. */
}
|BXorExpression "^" BAndExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_BXOR, $1, $3);
}
;

BAndExpression:
EqualExpression {
    /* Nothing to do. */
}
|BAndExpression "&" EqualExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_BAND, $1, $3);
}
;

EqualExpression:
RelExpression {
    /* Nothing to do. */
}
|EqualExpression "==" RelExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_EQ, $1, $3);
}
|EqualExpression "!=" RelExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_NE, $1, $3);
}
|EqualExpression "===" RelExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_EQ3, $1, $3);
}
|EqualExpression "!==" RelExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_NE3, $1, $3);
}
;

RelExpression:
CmpExpression {
    /* Nothing to do. */
}
|RelExpression "<" CmpExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_LT, $1, $3);
}
|RelExpression "<=" CmpExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_LE, $1, $3);
}
|RelExpression ">" CmpExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_GT, $1, $3);
}
|RelExpression ">=" CmpExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_GE, $1, $3);
}
;

CmpExpression:
OwnExpression {
    /* Nothing to do. */
}
|CmpExpression "<=>" OwnExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_CMP, $1, $3);
}
;

OwnExpression:
ShiftExpression {
    /* Nothing to do. */
}
|OwnExpression "~" ShiftExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_OWN_PROTO, $1, $3);
}
|OwnExpression "~~" ShiftExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_OWN_MEMBER, $1, $3);
}
;

ShiftExpression:
PlsMnsExpression {
    /* Nothing to do. */
}
|ShiftExpression "<<" PlsMnsExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_LSHIFT, $1, $3);
}
|ShiftExpression ">>" PlsMnsExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_RSHIFT, $1, $3);
}
;

PlsMnsExpression:
MulDivModExpression {
    /* Nothing to do. */
}
|PlsMnsExpression "+" MulDivModExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_ADD, $1, $3);
}
|PlsMnsExpression "-" MulDivModExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_SUB, $1, $3);
}
;

MulDivModExpression:
UnaryExpression {
    /* Nothing to do. */
}
|MulDivModExpression "*" UnaryExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_MUL, $1, $3);
}
|MulDivModExpression "/" UnaryExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_DIV, $1, $3);
}
|MulDivModExpression "%" UnaryExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_MOD, $1, $3);
}
;

UnaryExpression:
PostfixExpression {
    /* Nothing to do. */
}
|"!" UnaryExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_LNOT, $2, NULL);
}
|"~" UnaryExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_BNOT, $2, NULL);
}
|"~~" UnaryExpression {
    /* ~~をトークンにしたことによる特別措置
     * __invert__()を2回呼び出すか、
     * __abs__()に割り当てる予定 */
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_BNOT, $2, NULL);
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_BNOT, $$, NULL);
}
|"+" UnaryExpression {
    // 特に何もしない
    $$ = $2;
}
|"-" UnaryExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_NEGAT, $2, NULL);
}
|"++" UnaryExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_INC, $2, NULL);
}
|"--" UnaryExpression {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_DEC, $2, NULL);
}
;

PostfixExpression:
PrimaryExpression {
    /* Nothing to do. */
}
|PostfixExpression "++" {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_POST_INC, $1, NULL);
}
|PostfixExpression "--" {
    $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_POST_DEC, $1, NULL);
}
;

PrimaryExpression:
IntLiteral {
    /* Nothing to do. */
}
|FloatLiteral {
    /* Nothing to do. */
}
|StringLiteral {
    /* Nothing to do. */
}
|BlockLiteral {
    /* Nothing to do. */
}
|ObjectCreation {
    /* Nothing to do. */
}
|ArrayCreation {
    /* Nothing to do. */
}
|RangeCreation {
    /* Nothing to do. */
}
|Identifier {
    /* Nothing to do. */
}
|SlotAccess {
    /* Nothing to do. */
}
|Call {
    /* Nothing to do. */
}
|"(" Expression ")" {
    $$ = $2;
}
|NYU_TOK_ARGREF {
    $$ = nyu_tree_argref_new($1);
}
|NYU_TOK_ARGLEN {
    $$ = nyu_tree_arglen_new();
}
|NYU_TOK_CURBLK {
    $$ = nyu_tree_curblk_new();
}
;

IntLiteral:
NYU_TOK_INTVAL {
    $$ = nyu_tree_intval_new($1 << 2);
}
;

FloatLiteral:
NYU_TOK_FLTVAL {
    $$ = nyu_tree_fltval_new($1);
}
;

StringLiteral:
NYU_TOK_STRVAL {
    $$ = nyu_tree_strval_new($1);
}
;

BlockLiteral:
"{%" "%}" {
    $$ = nyu_tree_block_new(NULL);
}
|"{%" Statements SemicolonOpt "%}" {
    $$ = nyu_tree_block_new(nyu_tree_list_reverse($2));
}
;

SemicolonOpt:
/* nil */ {
    /* Nothing to do */
}
|";" {
    /* Nothing to do */
}
;

ObjectCreation:
"{" "}" {
    $$ = nyu_tree_object_new(NULL);
}
|"{" ObjectSlotList CommaOpt "}" {
    $$ = nyu_tree_object_new(nyu_tree_list_reverse($2));
}
;

ObjectSlotList:
ObjectSlot {
    $$ = nyu_tree_list_prepend(NULL, $1);
}
|ObjectSlotList "," ObjectSlot {
    $$ = nyu_tree_list_prepend($1, $3);
}
;

ObjectSlot:
NYU_TOK_SYMBOL ":" Expression {
    $$ = nyu_tree_object_slot_new($1, $3);
}
|NYU_TOK_STRVAL ":" Expression {
    $$ = nyu_tree_object_slot_new($1, $3);
}
;

ArrayCreation:
"[" "]" {
    $$ = nyu_tree_array_new(NULL);
}
|"[" Expressions CommaOpt "]" {
    $$ = nyu_tree_array_new(nyu_tree_list_reverse($2));
}
|"[" PrimaryExpression ".." PrimaryExpression "]" {
    /* TODO */
    $$ = nyu_tree_array_new(NULL);
}
|"[" PrimaryExpression ".." PrimaryExpression "/" PrimaryExpression "]" {
    /* TODO */
    $$ = nyu_tree_array_new(NULL);
}
|"[" PrimaryExpression "..." PrimaryExpression "]" {
    /* TODO */
    $$ = nyu_tree_array_new(NULL);
}
|"[" PrimaryExpression "..." PrimaryExpression "/" PrimaryExpression "]" {
    /* TODO */
    $$ = nyu_tree_array_new(NULL);
}
;

RangeCreation:
"(" PrimaryExpression ".." PrimaryExpression ")" {
    $$ = nyu_tree_range_new($2, $4, NULL, 1);
}
|"(" PrimaryExpression ".." PrimaryExpression "/" PrimaryExpression ")" {
    $$ = nyu_tree_range_new($2, $4, $6, 1);
}
|"(" PrimaryExpression "..." PrimaryExpression ")" {
    $$ = nyu_tree_range_new($2, $4, NULL, 0);
}
|"(" PrimaryExpression "..." PrimaryExpression "/" PrimaryExpression ")" {
    $$ = nyu_tree_range_new($2, $4, $6, 0);
}
;

Identifier:
NYU_TOK_SYMBOL {
    $$ = nyu_tree_symbol_new($1);
}
;

SlotAccess:
PrimaryExpression "." NYU_TOK_SYMBOL {
    $$ = nyu_tree_slot_access_new($1, $3);
}
|PrimaryExpression "[" Expression "]" {
    if ($3 && $3->type == NYU_TREE_TYPE_STRVAL) {
        $$ = nyu_tree_slot_access_new($1, NYU_TREE_STRVAL($3)->strval);
    } else {
        $$ = nyu_tree_expr_new(NYU_TREE_TYPE_EXPR_SLOT_ACCESS, $1, $3);
    }
}
|PrimaryExpression "[" PrimaryExpression ".." PrimaryExpression "]" {
    $$ = NULL;
}
|PrimaryExpression "[" PrimaryExpression ".." PrimaryExpression "/" PrimaryExpression "]" {
    $$ = NULL;
}
|PrimaryExpression "[" PrimaryExpression "..." PrimaryExpression "]" {
    $$ = NULL;
}
|PrimaryExpression "[" PrimaryExpression "..." PrimaryExpression "/" PrimaryExpression "]" {
    $$ = NULL;
}
;

Call:
PrimaryExpression "(" ")" {
    $$ = nyu_tree_call_new($1, NULL);
}
|PrimaryExpression "(" Expressions CommaOpt ")" {
    $$ = nyu_tree_call_new($1, nyu_tree_list_reverse($3));
}
;

Expressions:
Expression {
    $$ = nyu_tree_list_prepend(NULL, $1);
}
|Expressions "," Expression {
    $$ = nyu_tree_list_prepend($1, $3);
}
;

CommaOpt:
/* nil */ {
    /* Nothing to do. */
}
|"," {
    /* Nothing to do. */
}
;

%%

static NyuScope *scope_cache[NYU_SCOPE_CACHE_SIZE];
static int scope_cache_idx = 0;

static void push_scope(NyuTreeBlock *blk)
{
    NyuScope *scope;

    if (scope_cache_idx) {
        scope = scope_cache[--scope_cache_idx];
        scope->parent = current_scope;
    } else {
        scope = nyu_scope_new(current_scope);
    }

    scope->blk = NYU_TREE(blk);

    current_scope = scope;
}

static void pop_scope()
{
    NyuScopeVarList *l, *ll;
    NyuScope *scope;

    for (l = current_scope->scope_var_list; l; l = ll) {
        NyuVarData *d = (*(l->ref));
        *(l->ref) = d->parent;
        if ((d->val & 3) == 2) {
            /* TODO ちゃんとしたGCを実装する */
            nyu_refcnt_unref(NYU_REFCNT(d->val ^ 2));
        }
        free(d);
        ll = l->next;
        free(l);
    }

    scope = current_scope;
    current_scope = current_scope->parent;

    if (scope_cache_idx < (sizeof(scope_cache) / sizeof(NyuScope *))) {
        scope->scope_var_list = NULL;
        scope_cache[scope_cache_idx++] = scope;
    } else {
        free(scope);
    }
}

long *eval_tree_lvalue(NyuTree *tree, int undef_ok, int use_limit)
{
    switch (tree->type) {
    case NYU_TREE_TYPE_EXPR_PUT:
        /* TODO */
        break;

    case NYU_TREE_TYPE_EXPR_SET:
        /* TODO */
        break;

    case NYU_TREE_TYPE_EXPR_ADD_ASSIGN:

    case NYU_TREE_TYPE_EXPR_SUB_ASSIGN:

    case NYU_TREE_TYPE_EXPR_MUL_ASSIGN:

    case NYU_TREE_TYPE_EXPR_DIV_ASSIGN:

    case NYU_TREE_TYPE_EXPR_MOD_ASSIGN:

    case NYU_TREE_TYPE_EXPR_LSHIFT_ASSIGN:

    case NYU_TREE_TYPE_EXPR_RSHIFT_ASSIGN:

    case NYU_TREE_TYPE_EXPR_BAND_ASSIGN:

    case NYU_TREE_TYPE_EXPR_BXOR_ASSIGN:

    case NYU_TREE_TYPE_EXPR_BOR_ASSIGN:

    case NYU_TREE_TYPE_EXPR_LAND_ASSIGN:

    case NYU_TREE_TYPE_EXPR_LOR_ASSIGN:
        /* TODO */
        break;

    case NYU_TREE_TYPE_EXPR_SLOT_ACCESS:
        /* TODO */
        break;

    case NYU_TREE_TYPE_SLOT_ACCESS:
        {
            NyuTreeSlotAccess *sa = NYU_TREE_SLOT_ACCESS(tree);
            long prim = eval_tree(sa->prim, 0);
            switch (prim & 0x3) {
            case 0:
                /* 数値 */
                break;

            case 1:
                /* ネイティブ関数 */
                break;

            case 2:
                /* オブジェクト */
                return nyu_object_get_slot_lvalue(NYU_OBJECT(prim ^ 0x2),
                                                  NYU_OBJECT(sa->name),
                                                  !use_limit,
                                                  undef_ok);

            case 3:
                /* ブロック */
                break;
            }
        }
        break;

    case NYU_TREE_TYPE_SYMBOL:
        {
            NyuVarData *ref;

            ref = *(NYU_TREE_SYMBOL(tree)->ref);
            if (!ref || (use_limit && ref->scope != current_scope)) {
                if (!undef_ok) {
                    fprintf(stderr, "undefined variable %s\n",
                            NYU_TREE_SYMBOL(tree)->symbol);
                    abort();
                }

                *(NYU_TREE_SYMBOL(tree)->ref) = ref
                    = nyu_var_data_new(ref, 0, current_scope);

                current_scope->scope_var_list = nyu_scope_var_list_prepend(
                    current_scope->scope_var_list, NYU_TREE_SYMBOL(tree)->ref);
            }

            return &ref->val;
        }
        break;

    default:
        printf("DAME!!(%p): type=%d\n", tree, tree->type);
        break;
    }

    return NULL;
}

static void push_args(NyuTreeCall *call)
{
    NyuTreeList *l;
    long i;
    NyuScope *scope;

    /* 一時的に親スコープに戻す */
    scope = current_scope;
    current_scope = scope->parent;

    scope->argc = call->argc + 1;
    scope->argv[0] = current_scope->target;

    /* 引数を入れる */
    for (i = 1, l = call->args; l; l = l->next, i++) {
        scope->argv[i] = eval_tree(l->value, 0);
    }

    current_scope = scope;
}

static void update_args(NyuTreeCall *call, NyuTreeBlock *blk)
{
    NyuTreeList *l;
    long i;
    long tmp_argv[NYU_MAX_ARGLEN];

    tmp_argv[0] = current_scope->target;

    /* 引数を入れる */
    for (i = 1, l = call->args; l; l = l->next, i++) {
        tmp_argv[i] = eval_tree(l->value, 0);
    }

    current_scope->argc = call->argc + 1;
    memcpy(current_scope->argv, tmp_argv, current_scope->argc * sizeof(long));

    current_scope->blk = NYU_TREE(blk);
}

long eval_call(NyuTree *tree, int is_tail, NyuTree **tail)
{
    NyuTreeCall *call = NYU_TREE_CALL(tree);
    NyuTree *prim = call->prim;
    long ret = 0, val;

    *tail = NULL;

#define DO_CALL(X)                                                      \
    if (is_tail) {                                                      \
        /* tail callなのでスコープは作らない */                         \
        update_args(call, (X));                                         \
        *tail = NYU_TREE((X)->stmts);                                   \
    } else {                                                            \
        push_scope((X));                                                \
        push_args(call);                                                \
        ret = eval_tree(NYU_TREE((X)->stmts), 0);                       \
        pop_scope();                                                    \
    }

    switch (prim->type) {
    case NYU_TREE_TYPE_BLOCK:
        /* Blockの場合はcurrent_scopeのtargetを継承 */
        current_scope->target = current_scope->argv[0];
        DO_CALL(NYU_TREE_BLOCK(prim));
        break;

    case NYU_TREE_TYPE_CURBLK:
        /* 再帰の場合はcurrent_scopeのtargetを継承 */
        current_scope->target = current_scope->argv[0];
        {
            NyuTreeBlock *blk = NYU_TREE_BLOCK(current_scope->blk);
            DO_CALL(blk);
        }
        break;

    case NYU_TREE_TYPE_SYMBOL:
        /* 変数の場合はcurrent_scopeのtargetを継承 */
        current_scope->target = current_scope->argv[0];
        {
            NyuVarData *ref;

            ref = *(NYU_TREE_SYMBOL(prim)->ref);
            if (!ref) {
                fprintf(stderr, "undefined variable %s\n",
                        NYU_TREE_SYMBOL(prim)->symbol);
                abort();
            }

            val = ref->val;

            goto call_val;
        }
        break;

#if 0
/* ごめん。一旦おやすみ */
    case NYU_TREE_TYPE_EXPR_COND:
        /* 左辺値がCONDのときは最適化する */
        {
            NyuTreeCond *cond = NYU_TREE_COND(prim);
            NyuTreeBlock *blk;

            if (cond->tnode->type != NYU_TREE_TYPE_BLOCK
                || cond->fnode->type != NYU_TREE_TYPE_BLOCK) {
                /* 両方ともブロックじゃないならダメ */
                goto default_proc;
            }

            if (eval_tree(cond->cond, 0)) {
                blk = NYU_TREE_BLOCK(cond->tnode);
            } else {
                blk = NYU_TREE_BLOCK(cond->fnode);
            }

            DO_CALL(blk);
        }
        break;

    default_proc:
#endif

    default:
        /* その他の場合はeval時にtargetが決定される */
        current_scope->target = 0;
        val = eval_tree(prim, 0);

    call_val:
        switch (val & 3) {
        case 1:
            /* 下位1ビットが立っていたらきっとネイティブ関数 */
            {
                NyuFunc fn = (NyuFunc)(val ^ 1);

                push_scope(NULL);
                push_args(call);
                ret = fn(current_scope->argc, current_scope->argv);
                pop_scope();
            }
            break;

        case 2:
            /* オブジェクト */
            {
                NyuObject *obj = NYU_OBJECT(val ^ 2);

                /* TODO グローバル領域に用意しておく */
                NyuString *key = nyu_string_new("__call__");

                /* targetをこのオブジェクトにセット */
                current_scope->target = val;

                /* valを上書き */
                val = nyu_object_get_slot(obj, NYU_OBJECT(key));

                nyu_refcnt_unref(key);

                /* 再挑戦 */
                goto call_val;
            }
            break;

        case 3:
            /* 下位2ビットが立っていたらきっとブロック */
            {
                NyuTreeBlock *blk = NYU_TREE_BLOCK(val ^ 3);
                DO_CALL(blk);
            }
            break;

        default:
            fprintf(stderr, "object is not callable %s\n",
                    NYU_TREE_SYMBOL(prim)->symbol);
            abort();
        }
        break;
    }

#undef DO_CALL

    return ret;
}

static long cmp_int_obj(long v1, long v2)
{
    NyuObject *o2 = NYU_OBJECT(v2 ^ 2);
    NyuString *strval_int = nyu_string_new("__int__");
    long val = nyu_object_get_slot(o2, NYU_OBJECT(strval_int));
    long ret = 0;

  call_val:
    switch (val & 3) {
    case 0:
        /* 数値 */
        nyu_refcnt_unref(strval_int);
        /* TODO Integerオブジェクトの比較関数を呼び出す */
        return val;
    case 1:
        /* ネイティブ関数 */
        {
            NyuFunc fn = (NyuFunc)(val ^ 1);
            push_scope(NULL);
            current_scope->argc = 1;
            current_scope->argv[0] = current_scope->target = v2;
            ret = fn(current_scope->argc, current_scope->argv);
            pop_scope();
        }
        break;
    case 2:
        /* オブジェクト */
        {
            NyuObject *obj = NYU_OBJECT(val ^ 2);

            /* TODO グローバル領域に用意しておく */
            NyuString *key = nyu_string_new("__call__");

            /* targetをこのオブジェクトにセット */
            current_scope->target = val;

            /* valを上書き */
            val = nyu_object_get_slot(obj, NYU_OBJECT(key));

            nyu_refcnt_unref(key);

            /* 再挑戦 */
            goto call_val;
        }
        break;
    case 3:
        /* ブロック */
        {
            NyuTreeBlock *blk = NYU_TREE_BLOCK(val ^ 3);
            push_scope(blk);
            current_scope->argc = 1;
            current_scope->argv[0] = current_scope->target = v2;
            ret = eval_tree(NYU_TREE(blk->stmts), 0);
            pop_scope();
        }
    }

    nyu_refcnt_unref(strval_int);

    return 0;
}

static long cmp_obj_int(long v1, long v2, const char *name)
{
    NyuObject *o1 = NYU_OBJECT(v1 ^ 2);
    NyuString *strval = nyu_string_new(name);
    long val = nyu_object_get_slot(o1, NYU_OBJECT(strval));
    long ret = 0;

  call_val:
    switch (val & 3) {
    case 0:
        /* 数値 */
        nyu_refcnt_unref(strval);
        return val;
    case 1:
        /* ネイティブ関数 */
        {
            NyuFunc fn = (NyuFunc)(val ^ 1);
            push_scope(NULL);
            current_scope->argc = 2;
            current_scope->argv[0] = current_scope->target = v1;
            current_scope->argv[1] = v2;
            ret = fn(current_scope->argc, current_scope->argv);
            pop_scope();
        }
        break;
    case 2:
        /* オブジェクト */
        {
            NyuObject *obj = NYU_OBJECT(val ^ 2);

            /* TODO グローバル領域に用意しておく */
            NyuString *key = nyu_string_new("__call__");

            /* targetをこのオブジェクトにセット */
            current_scope->target = val;

            /* valを上書き */
            val = nyu_object_get_slot(obj, NYU_OBJECT(key));

            nyu_refcnt_unref(key);

            /* 再挑戦 */
            goto call_val;
        }
        break;
    case 3:
        /* ブロック */
        {
            NyuTreeBlock *blk = NYU_TREE_BLOCK(val ^ 3);
            push_scope(blk);
            current_scope->argc = 2;
            current_scope->argv[0] = current_scope->target = v1;
            current_scope->argv[1] = v2;
            ret = eval_tree(NYU_TREE(blk->stmts), 0);
            pop_scope();
        }
        break;
    }

    nyu_refcnt_unref(strval);

    return ret;
}

static long cmp_obj_obj(long v1, long v2, const char *name)
{
    NyuObject *o1 = NYU_OBJECT(v1 ^ 2);
    NyuString *strval = nyu_string_new(name);
    long val = nyu_object_get_slot(o1, NYU_OBJECT(strval));
    long ret = 0;

  call_val:
    switch (val & 3) {
    case 0:
        /* 数値 */
        nyu_refcnt_unref(strval);
        return val;
    case 1:
        /* ネイティブ関数 */
        {
            NyuFunc fn = (NyuFunc)(val ^ 1);
            push_scope(NULL);
            current_scope->argc = 2;
            current_scope->argv[0] = current_scope->target = v1;
            current_scope->argv[1] = v2;
            ret = fn(current_scope->argc, current_scope->argv);
            pop_scope();
        }
        break;
    case 2:
        /* オブジェクト */
        {
            NyuObject *obj = NYU_OBJECT(val ^ 2);

            /* TODO グローバル領域に用意しておく */
            NyuString *key = nyu_string_new("__call__");

            /* targetをこのオブジェクトにセット */
            current_scope->target = val;

            /* valを上書き */
            val = nyu_object_get_slot(obj, NYU_OBJECT(key));

            nyu_refcnt_unref(key);

            /* 再挑戦 */
            goto call_val;
        }
        break;
    case 3:
        /* ブロック */
        {
            NyuTreeBlock *blk = NYU_TREE_BLOCK(val ^ 3);
            push_scope(blk);
            current_scope->argc = 2;
            current_scope->argv[0] = current_scope->target = v1;
            current_scope->argv[1] = v2;
            ret = eval_tree(NYU_TREE(blk->stmts), 0);
            pop_scope();
        }
        break;
    }

    nyu_refcnt_unref(strval);

    return ret;
}

static long eval_expr_slot_access(NyuTree *tree)
{
    NyuTreeExpr *expr = NYU_TREE_EXPR(tree);
    NyuObject *obj;
    long v1, v2;
    long ret = 0;

    v1 = eval_tree(expr->lnode, 0);
    v2 = eval_tree(expr->rnode, 0);

    switch (v1 & 3) {
    case 0:
        /* 数値 */
        break;
    case 1:
        /* ネイティブ関数 */
        break;
    case 2:
        /* オブジェクト */
        obj = NYU_OBJECT(v1 ^ 2);
        if (obj->type == NYU_OBJECT_TYPE_ARRAY) {
            /* 配列へのアクセスは最適化しとく */
            switch (v2 & 3) {
            case 0:
                return (long)g_ptr_array_index(NYU_ARRAY(obj)->array, v2 >> 2);
            case 1:
                break;
            case 2:
                return nyu_object_get_slot(obj, NYU_OBJECT(v2 ^ 2));
            case 3:
                break;
            }
        } else if ((v2 & 3) == 0) {
            NyuString *strval = nyu_string_new("__getitem__");
            long val = nyu_object_get_slot(obj, NYU_OBJECT(strval));

          call_val:
            switch (val & 3) {
            case 0:
                /* 数値 */
                nyu_refcnt_unref(strval);
                return val;
            case 1:
                /* ネイティブ関数 */
                {
                    NyuFunc fn = (NyuFunc)(val ^ 1);
                    push_scope(NULL);
                    current_scope->argc = 2;
                    current_scope->argv[0] = current_scope->target = v1;
                    current_scope->argv[1] = v2;
                    ret = fn(current_scope->argc, current_scope->argv);
                    pop_scope();
                }
                break;
            case 2:
                /* オブジェクト */
                {
                    NyuObject *obj = NYU_OBJECT(val ^ 2);

                    /* TODO グローバル領域に用意しておく */
                    NyuString *key = nyu_string_new("__call__");

                    /* targetをこのオブジェクトにセット */
                    current_scope->target = val;

                    /* valを上書き */
                    val = nyu_object_get_slot(obj, NYU_OBJECT(key));

                    nyu_refcnt_unref(key);

                    /* 再挑戦 */
                    goto call_val;
                }
                break;
            case 3:
                /* ブロック */
                {
                    NyuTreeBlock *blk = NYU_TREE_BLOCK(val ^ 3);
                    push_scope(blk);
                    current_scope->argc = 2;
                    current_scope->argv[0] = current_scope->target = v1;
                    current_scope->argv[1] = v2;
                    ret = eval_tree(NYU_TREE(blk->stmts), 0);
                    pop_scope();
                }
                break;
            }

            nyu_refcnt_unref(strval);
        } else if ((v2 & 3) == 2) {
            return nyu_object_get_slot(obj, NYU_OBJECT(v2 ^ 2));
        }
        break;
    case 3:
        /* ブロック */
        break;
    }

    return ret;
}

long eval_tree(NyuTree *tree, int is_tail)
{
    long v1, v2;

tail_call:

    /* 空ブロックの場合など。後でnullオブジェクトに変える */
    if (!tree)
        return 0;

#define ASSIGNOP_REF(op,undef_ok,use_lim) {                            \
        NyuTreeExpr *expr;                                             \
        long *lv, rv;                                                  \
        expr = NYU_TREE_EXPR(tree);                                    \
        lv = eval_tree_lvalue(expr->lnode, (undef_ok), (use_lim));     \
        if (!lv) {                                                     \
            fprintf(stderr, "cannot get lvalue!\n");                   \
            abort();                                                   \
        }                                                              \
        rv = eval_tree(expr->rnode, is_tail);                          \
        if ((rv & 3) == 2)                                             \
            nyu_refcnt_ref(NYU_OBJECT(rv ^ 2));                        \
        if ((*lv & 3) == 2)                                            \
            nyu_refcnt_unref(NYU_OBJECT(*lv ^ 2));                     \
        *lv op rv;                                                     \
        return *lv;                                                    \
    }

#define ASSIGNOP(op,undef_ok,use_lim) {                                \
        NyuTreeExpr *expr;                                             \
        long *lv;                                                      \
        expr = NYU_TREE_EXPR(tree);                                    \
        lv = eval_tree_lvalue(expr->lnode, (undef_ok), (use_lim));     \
        if (!lv) {                                                     \
            fprintf(stderr, "cannot get lvalue!\n");                   \
            abort();                                                   \
        }                                                              \
        *lv op eval_tree(expr->rnode, is_tail);                        \
        return *lv;                                                    \
    }

#define ASSIGNOP_RSHIFT(op,undef_ok,use_lim) {                         \
        NyuTreeExpr *expr;                                             \
        long *lv;                                                      \
        expr = NYU_TREE_EXPR(tree);                                    \
        lv = eval_tree_lvalue(expr->lnode, (undef_ok), (use_lim));     \
        if (!lv) {                                                     \
            fprintf(stderr, "cannot get lvalue!\n");                   \
            abort();                                                   \
        }                                                              \
        *lv op (eval_tree(expr->rnode, is_tail) >> 2);                 \
        return *lv;                                                    \
    }

#define ASSIGNOP_RSHIFT_CLR(op,undef_ok,use_lim) {                     \
        NyuTreeExpr *expr;                                             \
        long *lv;                                                      \
        expr = NYU_TREE_EXPR(tree);                                    \
        lv = eval_tree_lvalue(expr->lnode, (undef_ok), (use_lim));     \
        if (!lv) {                                                     \
            fprintf(stderr, "cannot get lvalue!\n");                   \
            abort();                                                   \
        }                                                              \
        *lv op (eval_tree(expr->rnode, is_tail) >> 2);                 \
        *lv &= ~0x3L;                                                  \
        return *lv;                                                    \
    }


#define BINOP(op)                                     \
    v1 = eval_tree(NYU_TREE_EXPR(tree)->lnode, 0);    \
    v2 = eval_tree(NYU_TREE_EXPR(tree)->rnode, 0);    \
    switch (v1 & 3) {                                 \
    case 0:                                           \
        switch (v2 & 3) {                             \
        case 0:                                       \
            /* 数値x数値 */                           \
            return v1 op v2;                          \
        }                                             \
        break;                                        \
    }                                                 \
    return 0;

#define BINOP_RSHIFT(op)                              \
    v1 = eval_tree(NYU_TREE_EXPR(tree)->lnode, 0);    \
    v2 = eval_tree(NYU_TREE_EXPR(tree)->rnode, 0);    \
    switch (v1 & 3) {                                 \
    case 0:                                           \
        switch (v2 & 3) {                             \
        case 0:                                       \
            /* 数値x数値 */                           \
            return v1 op (v2 >> 2);                   \
        }                                             \
        break;                                        \
    }                                                 \
    return 0;

#define BINOP_RSHIFT_CLR(op)                          \
    v1 = eval_tree(NYU_TREE_EXPR(tree)->lnode, 0);    \
    v2 = eval_tree(NYU_TREE_EXPR(tree)->rnode, 0);    \
    switch (v1 & 3) {                                 \
    case 0:                                           \
        switch (v2 & 3) {                             \
        case 0:                                       \
            /* 数値x数値 */                           \
            return (v1 op (v2 >> 2)) & ~0x3L;         \
        }                                             \
        break;                                        \
    }                                                 \
    return 0;

#define BINOP_CMPOP(op,name)                          \
    v1 = eval_tree(NYU_TREE_EXPR(tree)->lnode, 0);    \
    v2 = eval_tree(NYU_TREE_EXPR(tree)->rnode, 0);    \
    switch (((v1 & 3) << 2) | (v2 & 3)) {             \
    case 0:                                           \
        /* 数値x数値 */                               \
        return (v1 op v2) << 2;                       \
    case 1:                                           \
        /* 数値xネイティブ関数 */                     \
        break;                                        \
    case 2:                                           \
        /* 数値xオブジェクト */                       \
        return cmp_int_obj(v1, v2);                   \
    case 3:                                           \
        /* 数値xブロック */                           \
        break;                                        \
    case 4:                                           \
        /* ネイティブ関数x数値 */                     \
        break;                                        \
    case 5:                                           \
        /* ネイティブ関数xネイティブ関数 */           \
        break;                                        \
    case 6:                                           \
        /* ネイティブ関数xオブジェクト */             \
        break;                                        \
    case 7:                                           \
        /* ネイティブ関数xブロック */                 \
        break;                                        \
    case 8:                                           \
        /* オブジェクトx数値 */                       \
        return cmp_obj_int(v1, v2, name);             \
    case 9:                                           \
        /* オブジェクトxネイティブ関数 */             \
        break;                                        \
    case 10:                                          \
        /* オブジェクトxオブジェクト */               \
        return cmp_obj_obj(v1, v2, name);             \
    case 11:                                          \
        /* オブジェクトxブロック */                   \
        break;                                        \
    case 12:                                          \
        /* ブロックx数値 */                           \
        break;                                        \
    case 13:                                          \
        /* ブロックxネイティブ関数 */                 \
        break;                                        \
    case 14:                                          \
        /* ブロックxオブジェクト */                   \
        break;                                        \
    case 15:                                          \
        /* ブロックxブロック */                       \
        break;                                        \
    }                                                 \
    return 0;

    switch (tree->type) {
    case NYU_TREE_TYPE_LIST:
        {
            NyuTreeList *l = NYU_TREE_LIST(tree);

            /* 最後の一個前まで処理 */
            for (; l->next; l = l->next) {
                eval_tree(l->value, 0);
            }

            tree = l->value;
            is_tail = 1;
            goto tail_call;
        }
        break;

    case NYU_TREE_TYPE_EXPR_PUT:
        ASSIGNOP_REF(=, 1, 1)
        break;

    case NYU_TREE_TYPE_EXPR_SET:
        ASSIGNOP_REF(=, 1, 0)
        break;

    case NYU_TREE_TYPE_EXPR_ADD_ASSIGN:
        ASSIGNOP(+=, 0, 0)
        break;

    case NYU_TREE_TYPE_EXPR_SUB_ASSIGN:
        ASSIGNOP(-=, 0, 0)
        break;

    case NYU_TREE_TYPE_EXPR_MUL_ASSIGN:
        ASSIGNOP_RSHIFT(*=, 0, 0)
        break;

    case NYU_TREE_TYPE_EXPR_DIV_ASSIGN:
        ASSIGNOP_RSHIFT(/=, 0, 0)
        break;

    case NYU_TREE_TYPE_EXPR_MOD_ASSIGN:
        ASSIGNOP(%=, 0, 0)
        break;

    case NYU_TREE_TYPE_EXPR_LSHIFT_ASSIGN:
        ASSIGNOP_RSHIFT(<<=, 0, 0)
        break;

    case NYU_TREE_TYPE_EXPR_RSHIFT_ASSIGN:
        ASSIGNOP_RSHIFT_CLR(>>=, 0, 0)
        break;

    case NYU_TREE_TYPE_EXPR_BAND_ASSIGN:
        ASSIGNOP(&=, 0, 0)
        break;

    case NYU_TREE_TYPE_EXPR_BXOR_ASSIGN:
        ASSIGNOP(^=, 0, 0)
        break;

    case NYU_TREE_TYPE_EXPR_BOR_ASSIGN:
        ASSIGNOP(|=, 0, 0)
        break;

    case NYU_TREE_TYPE_EXPR_LAND_ASSIGN:

    case NYU_TREE_TYPE_EXPR_LOR_ASSIGN:

    case NYU_TREE_TYPE_EXPR_SWAP:
        break;

    case NYU_TREE_TYPE_EXPR_COND:
        {
            NyuTreeCond *cond = NYU_TREE_COND(tree);
            long val;
            val = eval_tree(cond->cond, 0);
            if ((val & 3) == 2) {
                /* TODO オブジェクトの場合は__bool__を呼び出す */
                tree = cond->tnode;
            } else {
                tree = val ? cond->tnode : cond->fnode;
            }
            goto tail_call;
        }
        break;

    case NYU_TREE_TYPE_EXPR_LOR:
        BINOP(||)
        break;

    case NYU_TREE_TYPE_EXPR_LXOR:
        /* ??? */
        break;

    case NYU_TREE_TYPE_EXPR_LAND:
        BINOP(&&)
        break;

    case NYU_TREE_TYPE_EXPR_BOR:
        BINOP(|)
        break;

    case NYU_TREE_TYPE_EXPR_BXOR:
        BINOP(^)
        break;

    case NYU_TREE_TYPE_EXPR_BAND:
        BINOP(&)
        break;

    case NYU_TREE_TYPE_EXPR_EQ:
        BINOP_CMPOP(==, "__eq__")
        break;

    case NYU_TREE_TYPE_EXPR_NE:
        BINOP_CMPOP(!=, "__ne__")
        break;

    case NYU_TREE_TYPE_EXPR_EQ3:

    case NYU_TREE_TYPE_EXPR_NE3:
        break;

    case NYU_TREE_TYPE_EXPR_LT:
        BINOP_CMPOP(<, "__lt__")
        break;

    case NYU_TREE_TYPE_EXPR_LE:
        BINOP_CMPOP(<=, "__le__")
        break;

    case NYU_TREE_TYPE_EXPR_GT:
        BINOP_CMPOP(>, "__gt__")
        break;

    case NYU_TREE_TYPE_EXPR_GE:
        BINOP_CMPOP(>=, "__ge__")
        break;

    case NYU_TREE_TYPE_EXPR_CMP:

    case NYU_TREE_TYPE_EXPR_OWN_PROTO:

    case NYU_TREE_TYPE_EXPR_OWN_MEMBER:
        break;

    case NYU_TREE_TYPE_EXPR_LSHIFT:
        BINOP_RSHIFT(<<)
        break;

    case NYU_TREE_TYPE_EXPR_RSHIFT:
        BINOP_RSHIFT_CLR(>>)
        break;

    case NYU_TREE_TYPE_EXPR_ADD:
        BINOP(+)
        break;

    case NYU_TREE_TYPE_EXPR_SUB:
        BINOP(-)
        break;

    case NYU_TREE_TYPE_EXPR_MUL:
        BINOP_RSHIFT(*)
        break;

    case NYU_TREE_TYPE_EXPR_DIV:
        BINOP_RSHIFT(/)
        break;

    case NYU_TREE_TYPE_EXPR_MOD:
        BINOP(%)
        break;

    case NYU_TREE_TYPE_EXPR_LNOT:

    case NYU_TREE_TYPE_EXPR_BNOT:

    case NYU_TREE_TYPE_EXPR_NEGAT:

    case NYU_TREE_TYPE_EXPR_INC:

    case NYU_TREE_TYPE_EXPR_DEC:

    case NYU_TREE_TYPE_EXPR_POST_INC:

    case NYU_TREE_TYPE_EXPR_POST_DEC:
        break;

    case NYU_TREE_TYPE_EXPR_SLOT_ACCESS:
        return eval_expr_slot_access(tree);

    case NYU_TREE_TYPE_SLOT_ACCESS:
        {
            NyuTreeSlotAccess *sa = NYU_TREE_SLOT_ACCESS(tree);
            long prim = eval_tree(sa->prim, 0);

            /* targetをセットしておく */
            current_scope->target = prim;

            switch (prim & 0x3) {
            case 0:
                /* 数値 */
                break;

            case 1:
                /* ネイティブ関数 */
                break;

            case 2:
                /* オブジェクト */
                return nyu_object_get_slot(NYU_OBJECT(prim ^ 0x2),
                                           NYU_OBJECT(sa->name));

            case 3:
                /* ブロック */
                break;
            }
        }
        break;

    case NYU_TREE_TYPE_CALL:
        {
            NyuTree *tail; /* <- 呼び出し先で初期化される */
            long ret = eval_call(tree, is_tail, &tail);
            if (tail) {
                is_tail = 0;
                tree = tail;
                goto tail_call;
            }
            return ret;
        }
        break;

    case NYU_TREE_TYPE_BLOCK:
        return ((long)tree | 0x3);

    case NYU_TREE_TYPE_OBJECT:
        {
            NyuTreeList *l;
            NyuObject *obj = nyu_object_new();

            for (l = NYU_TREE_OBJECT(tree)->slots; l; l = l->next) {
                NyuTreeObjectSlot *s = NYU_TREE_OBJECT_SLOT(l->value);
                NyuString *key = s->name;
                long val = eval_tree(s->expr, 0);
                nyu_object_put_slot(obj, NYU_OBJECT(key), val);
            }

            /* refに備えて強制的に参照カウントを0にしておく */
            NYU_REFCNT(obj)->count = 0;

            return ((long)obj | 0x2);
        }
        break;

    case NYU_TREE_TYPE_OBJECT_SLOT:
        /* ここにはこない */
        break;

    case NYU_TREE_TYPE_ARRAY:
        {
            NyuTreeArray *ta = NYU_TREE_ARRAY(tree);
            NyuArray *array;
            NyuTreeList *l;
            GPtrArray *data;

            array = nyu_array_sized_new(ta->len);
            data = array->array;

            for (l = ta->elms; l; l = l->next) {
                g_ptr_array_add(data, (gpointer)eval_tree(l->value, 0));
            }

            return ((long)array | 2);
        }
        break;

    case NYU_TREE_TYPE_RANGE:
        {
            NyuRange *range;
            long from, to, step;

            from = eval_tree(NYU_TREE_RANGE(tree)->from, 0);
            if ((from & 3) != 0) {
                fprintf(stderr, "Sorry! Range data must be int");
                abort();
            }

            to = eval_tree(NYU_TREE_RANGE(tree)->to, 0);
            if ((to & 3) != 0) {
                fprintf(stderr, "Sorry! Range data must be int");
                abort();
            }

            if (NYU_TREE_RANGE(tree)->step) {
                step = eval_tree(NYU_TREE_RANGE(tree)->step, 0);
                if ((step & 3) != 0) {
                    fprintf(stderr, "Sorry! Range data must be int");
                    abort();
                }

                step >>= 2;
            } else {
                step = (to < from) ? -1 : 1;
            }

            range = nyu_range_new(from >> 2, to >> 2, step,
                                  NYU_TREE_RANGE(tree)->is_include_border);

            return ((long)range | 2);
        }
        break;

    case NYU_TREE_TYPE_SYMBOL:
        {
            NyuVarData *ref;

            ref = *(NYU_TREE_SYMBOL(tree)->ref);
            if (!ref) {
                fprintf(stderr, "undefined variable %s\n",
                        NYU_TREE_SYMBOL(tree)->symbol);
                abort();
            }

            return ref->val;
        }
        break;

    case NYU_TREE_TYPE_STRVAL:
        return ((long)NYU_TREE_STRVAL(tree)->strobj | 0x2);

    case NYU_TREE_TYPE_INTVAL:
        return NYU_TREE_INTVAL(tree)->intval;

    case NYU_TREE_TYPE_FLTVAL:
        /* TODO */
        break;

    case NYU_TREE_TYPE_ARGREF:
        /* 範囲外の場合は0を返す。後でnullオブジェクトに変えるべき */
        if (NYU_TREE_ARGREF(tree)->idx < 0
            || current_scope->argc <= NYU_TREE_ARGREF(tree)->idx)
            return 0;
        return current_scope->argv[NYU_TREE_ARGREF(tree)->idx];

    case NYU_TREE_TYPE_ARGLEN:
        return current_scope->argc << 2;

    case NYU_TREE_TYPE_CURBLK:
        return ((long)current_scope->blk | 0x3);

    default:
        printf("UNKNOWN(%p): type=%d\n", tree, tree->type);
        break;
    }

#undef BINOP

    return 0;
}

void yyerror(const char *msg)
{
    fprintf(stderr, "Error: %s\n", msg);
    abort();
}

/* 仮 */
long nyu_range_each(long argc, long *argv)
{
    NyuObject *obj;
    NyuRange *self;
    long val;
    long i, j, k;
    long ret = 0;

    if (argc < 2)
        return 0;

    if ((argv[0] & 3) != 2)
        return 0;

    obj = NYU_OBJECT(argv[0] ^ 2);
    if (obj->type != NYU_OBJECT_TYPE_RANGE)
        return 0;

    self = NYU_RANGE(obj);

#define DO_LOOP2(op,expr)                       \
    for (; i op j; i += k) {                    \
        current_scope->argv[1] = i << 2;        \
        ret = expr;                             \
    }

#define DO_LOOP(expr)                           \
    if (k > 0) {                                \
        if (self->is_include_border) {          \
            DO_LOOP2(<=, expr);                 \
        } else {                                \
            DO_LOOP2(<, expr);                  \
        }                                       \
    } else {                                    \
        if (self->is_include_border) {          \
            DO_LOOP2(>=, expr);                 \
        } else {                                \
            DO_LOOP2(>, expr);                  \
        }                                       \
    }

    val = argv[1];

    i = self->from;
    j = self->to;
    k = self->step;

  call_val:
    switch (val & 3) {
    case 1:
        /* 下位1ビットが立っていたらきっとネイティブ関数 */
        {
            NyuFunc fn = (NyuFunc)(val ^ 1);

            push_scope(NULL);
            current_scope->argc = 2;
            DO_LOOP(fn(current_scope->argc, current_scope->argv));
            pop_scope();
        }
        break;

    case 2:
        /* オブジェクト */
        {
            NyuObject *obj = NYU_OBJECT(val ^ 2);

            /* TODO グローバル領域に用意しておく */
            NyuString *key = nyu_string_new("__call__");

            /* targetをこのオブジェクトにセット */
            current_scope->target = val;

            /* valを上書き */
            val = nyu_object_get_slot(obj, NYU_OBJECT(key));

            nyu_refcnt_unref(key);

            /* 再挑戦 */
            goto call_val;
        }
        break;

    case 3:
        /* 下位2ビットが立っていたらきっとブロック */
        {
            NyuTreeBlock *blk;
            NyuTreeList *stmts;

            blk = NYU_TREE_BLOCK(val ^ 3);
            stmts = blk->stmts;
            push_scope(blk);
            current_scope->argc = 2;
            DO_LOOP(eval_tree(NYU_TREE(stmts), 0));
            pop_scope();
        }
        break;

    default:
        fprintf(stderr, "object is not callable\n");
        abort();
    }

#undef DO_LOOP
#undef DO_LOOP2

    return ret;
}
